/**
 * @author salim & sidyous
 */

import java.io.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SharedObject implements Serializable, SharedObject_itf {
	
	/*
	 * Attributes
	 */
	
	private static final long serialVersionUID = 1L;
	
	// Core
	Object obj;
	int id;
	int lock;
	
	// Synchronization
	Lock psy;
	Lock chik;
	Condition crw;
	
	// Constant
	static final int NL = 0;
	static final int RLC = 1;
	static final int RLT = 2;
	static final int WLC = 3;
	static final int WLT = 4;
	static final int RLT_WLC = 5;
	
	/*
	 * Methods
	 */
	
	// Constructor
	public SharedObject(Object obj, int id) {
		super();
		//System.out.println("SharedObject : SharedObject " + id);
		this.obj = obj;
		this.id = id;
		lock = NL;
		psy = new ReentrantLock();
		chik = new ReentrantLock();
		crw = psy.newCondition();
	}

	// invoked by the user program on the client node
	public void lock_read() {
		//System.out.println("SharedObject : lock_read");
		chik.lock();
		psy.lock();
		//System.out.println("SharedObject : lock_read : CRITICAL SECTION");
		//System.out.println(lock);
		switch(lock)
		{
		case NL:
			obj = Client.lock_read(id);
			lock = RLT;
			break;
		case RLC:
			lock = RLT;
			break;
		case RLT:
			//System.out.println("ERROR : SharedObject.lock_read() : Doesn't handle imbricated locks");
			break;
		case WLC:
			lock = RLT_WLC;
			break;
		case WLT:
			//System.out.println("ERROR : SharedObject.lock_read() : Doesn't handle imbricated locks");
			break;
		case RLT_WLC:
			//System.out.println("ERROR : SharedObject.lock_read() : Doesn't handle imbricated locks");
			break;
		}
		
		psy.unlock();
		chik.unlock();
		//System.out.println("SharedObject : lock_read : CRITICAL SECTION OUT");
	}

	// invoked by the user program on the client node
	public void lock_write() {
		//System.out.println("SharedObject : lock_write");
		
		boolean b = false;
		Object tmp;
		
		while (!b)
		{
			chik.lock();
			psy.lock();
			//System.out.println("SharedObject : lock_write : CRITICAL SECTION");
			//System.out.println(lock);
			switch (lock) {
			case NL:
//				System.out.println("AAAAAAAAAAAA : NL");
				tmp = Client.lock_write(id);
				if (tmp == null) break;
				obj = tmp;
				lock = WLT;
				b = true;
				break;
			case RLC:
//				System.out.println("AAAAAAAAAAAA : RLC");
				tmp = Client.lock_write(id);
				if (tmp == null) break;
				obj = tmp;
				lock = WLT;
				b = true;
				break;
			case RLT:
//				System.out.println("AAAAAAAAAAAA : RLT");
				//System.out.println("ERROR : SharedObject.lock_write() : doesn't handle imbricated locks");
				b = true;
				break;
			case WLC:
//				System.out.println("AAAAAAAAAAAA : WLC");
				lock = WLT;
				b = true;
				break;
			case WLT:
//				System.out.println("AAAAAAAAAAAA : WLT");
				//System.out.println("ERROR : SharedObject.lock_write() : doesn't handle imbricated locks");
				b = true;
				break;
			case RLT_WLC:
//				System.out.println("AAAAAAAAAAAA : RLT_WLC");
				//System.out.println("ERROR : SharedObject.lock_write() : doesn't handle imbricated locks");
				b = true;
				break;
			}
			//System.out.println("SharedObject : lock_write : B = " + b + " obj = " + obj);
			psy.unlock();
			chik.unlock();
			//System.out.println("SharedObject : lock_write : CRITICAL SECTION OUT");
		}
	}

	// invoked by the user program on the client node
	public synchronized void unlock() {
		//System.out.println("SharedObject : unlock");
		
		psy.lock();
		//System.out.println("SharedObject : unlock : CRITICAL SECTION");
		//System.out.println(lock);
		switch(lock)
		{
		case NL:
			//System.out.println("ERROR : SharedObject.unlock() : Lock not taken");
			break;
		case RLC:
			//System.out.println("ERROR : SharedObject.unlock() : Lock not taken");
			break;
		case RLT:
			lock = RLC;
			break;
		case WLC:
			//System.out.println("ERROR : SharedObject.unlock() : Lock not taken");
			break;
		case WLT:
			lock = WLC;
			break;
		case RLT_WLC:
			lock = WLC;
			break;
		}
		
		crw.signalAll();
		psy.unlock();
		//System.out.println("SharedObject : unlock : CRITICAL SECTION OUT");
	}


	// callback invoked remotely by the server
	public  Object reduce_lock() {
		//System.out.println("SharedObject : reduce_lock");
		chik.lock();
		psy.lock();
		//System.out.println("SharedObject : reduce_lock : CRITICAL SECTION");
		//System.out.println(lock);
		switch(lock)
		{
		case NL:
			//System.out.println("ERROR : SharedObject.reduce_lock() : Lock not taken");
			break;
		case RLC:
			lock = NL;
			break;
		case RLT:
			crw.awaitUninterruptibly();
			lock = NL;
			break;
		case WLC:
			lock = RLC;
			break;
		case WLT:
			crw.awaitUninterruptibly();
			lock = RLC;
			break;
		case RLT_WLC:
			crw.awaitUninterruptibly();
			lock = RLC;
			break;
		}
		
		psy.unlock();
		//System.out.println("SharedObject : reduce_lock : CRITICAL SECTION OUT");
		chik.unlock();
		return obj;
	}

	// callback invoked remotely by the server
	public  void invalidate_reader() {
		//System.out.println("SharedObject : invalidate_reader");
		chik.lock();
		psy.lock();
		//System.out.println("SharedObject : invalidate_reader : CRITICAL SECTION");
		//System.out.println(lock);
		switch(lock)
		{
		case NL:
			//System.out.println("ERROR : SharedObject.invalidate_reader() : No reading rights");
			break;
		case RLC:
			lock = NL;
			break;
		case RLT:
			crw.awaitUninterruptibly();
			lock = NL;
			break;
		case WLC:
			//System.out.println("ERROR : SharedObject.invalidate_reader() : need to call invalidate_writer instead");
			break;
		case WLT:
			//System.out.println("ERROR : SharedObject.invalidate_reader() : need to call invalidate_writer instead");
			break;
		case RLT_WLC:
			//System.out.println("ERROR : SharedObject.invalidate_reader() : need to call invalidate_writer instead");
			break;
		}
		
		psy.unlock();
		chik.unlock();
		//System.out.println("SharedObject : invalidate_reader : CRITICAL SECTION OUT");
	}

	public  Object invalidate_writer() {
		//System.out.println("SharedObject : invalidate_writer");
		chik.lock();
		psy.lock();
		//System.out.println("SharedObject : invalidate_writer : CRITICAL SECTION");
		//System.out.println(lock);
		switch(lock)
		{
		case NL:
			//System.out.println("ERROR : SharedObject.invalidate_writer() : No writing rights");
			break;
		case RLC:
			//System.out.println("ERROR : SharedObject.invalidate_writer() : No writing rights");
			break;
		case RLT:
			//System.out.println("ERROR : SharedObject.invalidate_writer() : No writing rights");
			break;
		case WLC:
			lock = NL;
			break;
		case WLT:
			crw.awaitUninterruptibly();
			lock = NL;
			break;
		case RLT_WLC:
			//System.out.println("SharedObject : invalidate_writer : WAIT");
			crw.awaitUninterruptibly();
			//System.out.println("SharedObject : invalidate_writer : SIGNALed");
			lock = NL;
			break;
		}
		
		psy.unlock();
		chik.unlock();
		//System.out.println("SharedObject : invalidate_writer : CRITICAL SECTION OUT");
		
		return obj;
	}
}
